#!/usr/bin/perl
use strict;
use FindBin;
use Cwd 'abs_path';
use Getopt::Long;
use Digest::MD5 qw(md5 md5_hex);
use JSON qw(to_json from_json);

use DeployUtils;
use TagentClient;

Getopt::Long::Configure("pass_through");

sub usage {
    my $pname = $FindBin::Script;

    print("Usage: $pname [--verbose 0|1]\n");
    print("              [--async 0|1] [--eofstr EOFString]\n");
    print("              [--destencoding ServerSideEncoding] [--timeout TimeoutSecs]\n");
    print("              --interpreter sh|perl|... --script ScriptJson\n");
    print("\n");
    print("       --node:         Host node json\n");
    print("       --async:        execute remote cmd asynchronized\n");
    print("       --eofstr:       when command outpute match eofstr, the execute will be terminated\n");
    print("       --destencoding: Server Side encoding\n");
    print("       --timeout:      tiemout for ssh execute\n");
    print("       --interpreter:  Script interperter, such as sh|perl|vbs|powershell\n");
    print("       --script:       Script content, example: '#!/bin/bash\\necho helloworld!\\n\"'\n");

    exit(1);
}

# windows测试target server是否可连接
# tagentexec --inspath "qqqq" -h 192.168.0.178 -p 3939 -u tagent --pass ts9012501  "dir C:"
sub main {
    my ( $isHelp, $isVerbose, $envPath, $version );
    my ( $node, $async, $scriptContent, $eofStr, $serverCharset, $timeOut );
    my $interpreter = 'sh';
    $isVerbose = 0;
    $async     = 0;
    $timeOut   = 0;

    GetOptions(
        'h|help'         => \$isHelp,
        'v|verbose=i'    => \$isVerbose,
        'node=s'         => \$node,
        'async=i'        => \$async,
        'eofstr=s'       => \$eofStr,
        'timeout=i'      => \$timeOut,
        'destencoding=s' => \$serverCharset,
        'interpreter=s'  => \$interpreter,
        'script=s'       => \$scriptContent
    );

    usage() if ( defined($isHelp) );
    my $optionError = 0;

    my $deployUtils = DeployUtils->new();

    my $nodeInfo = $deployUtils->getNodeInfo($node);
    if ( not $nodeInfo ) {
        $optionError = 1;
        print("ERROR: Execute node json not defined by environment AUTOEXEC_NODE or option --node\n");
    }

    if ( not defined($scriptContent) ) {
        $optionError = 1;
        print("ERROR: Must defined script content with --script option.\n");
    }

    if ( $optionError == 1 ) {
        usage();
    }

    my $hasError = 0;

    my $host    = $nodeInfo->{host};
    my $port    = $nodeInfo->{protocolPort};
    my $user    = $nodeInfo->{username};
    my $pass    = $nodeInfo->{password};
    my $insId   = $nodeInfo->{resourceId};
    my $insName = $nodeInfo->{nodeName};

    if ( defined($pass) and $pass ne '' ) {
        $pass = $deployUtils->decryptPwd($pass);
    }

    my $tagent = new TagentClient( $host, $port, $pass, $timeOut, $timeOut, $serverCharset );

    $scriptContent =~ s/\\n/\n/sg;
    my $scriptInterpreter = $interpreter;
    my $arguments         = '';
    foreach my $arg (@ARGV) {
        $arguments = qq{$arguments "$arg"};
    }

    while ( $arguments =~ /(?<!\\)\$(\w+)/g ) {
        my $varName = $1;
        if ( exists( $ENV{$varName} ) ) {
            $arguments =~ s/(?<!\\)\$$varName/$ENV{$varName}/g;
        }
    }

    while ( $arguments =~ /(?<!\\)\$\{(\w+)\}/g ) {
        my $varName = $1;
        if ( exists( $ENV{$varName} ) ) {
            $arguments =~ s/(?<!\\)\$\{$varName\}/$ENV{$varName}/g;
        }
    }

    my $hasError = 0;

    my $randStr;
    $randStr = $randStr . sprintf( "%x", rand(16) ) for 1 .. 4;
    my $scriptName = md5_hex($scriptContent) . "-$randStr" . $deployUtils->getScriptExtName($scriptInterpreter);

    my $ret = $tagent->writeFile( $user, $scriptContent, '$TMPDIR/' . $scriptName, 1, 1 );
    if ( $ret != 0 ) {
        die("ERROR: Can not connect to tagent.\n");
    }

    my $agentOsType = $tagent->{agentOsType};
    my $envLine     = '';

    my $namePath = $ENV{DEPLOY_PATH};
    my $idPath   = $ENV{DEPLOY_ID_PATH};
    if ( defined($namePath) and $namePath ne '' ) {
        my $insIdPath   = "$idPath/$insId";
        my $insNamePath = "$namePath/$insName";
        if ( $agentOsType eq 'windows' ) {
            $envLine = "set TS_INSID=$insIdPath && set TS_INSNAME=\"$insNamePath\" \&\& ";
        }
        else {
            $envLine = "export TS_INSNAME=\"$insNamePath\" || setenv TS_INSNAME \"$insNamePath\"; export TS_INSID=$insIdPath || setenv TS_INSID $insIdPath \&\& ";
        }
    }

    my $cmd = '';
    my $delCmd;
    if ( $agentOsType eq 'windows' ) {
        $cmd    = "$envLine$scriptInterpreter %TMPDIR%\\$scriptName $arguments";
        $delCmd = "del %TMPDIR%\\$scriptName";
    }
    else {
        $cmd = "$envLine$scriptInterpreter \$TMPDIR/$scriptName $arguments; rc=\$?; rm -f \$TMPDIR/$scriptName; exit \$rc";
    }

    print("INFO: Try to execute script by tagent...\n");
    my $exitStatus = -1;
    if ( $async == 1 ) {
        $exitStatus = $tagent->execCmdAsync( $user, $cmd, 1, $eofStr );
    }
    else {
        $exitStatus = $tagent->execCmd( $user, $cmd, 1, $eofStr );
    }

    $hasError = $hasError + $exitStatus;
    if ( $exitStatus eq 0 ) {
        print("FINE: Tagent remote exec success.\n");
    }
    else {
        print("ERROR: Tagent remote exec has error.\n");
    }

    if ( defined($delCmd) ) {
        $tagent->execCmd( $user, $delCmd, 0 );
    }

    return $hasError;
}

exit main();
